package controller;

/**
 * The GameController provides all the methods needed to update the GameDisplay class
 * and receive input from it. It acts as an intermediate between the game GUI and the server.
 */

import gui.Display;
import gui.GameDisplay;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;

import model.Card;
import model.TableInformation;
import client.networking.ClientCallback;
import client.networking.ClientCallbackInterface;

public class GameController extends Controller implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private GameDisplay gameDisplay;
	private int tableID;
	private TableInformation table;
	private String owner;
	
	public GameController(Display display) {
		GameDisplay gameDisplay = new GameDisplay(this, display);
		setDisplay(gameDisplay);
		this.gameDisplay = gameDisplay;
	}
	
	//Getters and setters

	public GameDisplay getGameDisplay() {
		return gameDisplay;
	}

	public void setOwner(String username) {
		this.owner = username;
	}

	public int getNumberOfPlayers() {
		return table.getPlayers().size();
	}
	
	private void displayName(int seat, String name) {
		gameDisplay.setPlayerName(seat, name);
		
	}

	/**
	 * Remove the player from the table and return to the lobby screen
	 * by giving control to the lobby controller
	 * @throws RemoteException
	 */
	public void returnToLobby() throws RemoteException {
		serverRemote.removePlayerFromTable(tableID, user.getUsername(), 50);		
		Controller lobbyController = new LobbyController(display);
		this.giveControlTo(lobbyController);
		
		this.sendMessage("--> " + user.getUsername() + " has left the game.");

	}
	
	/**
	 * Update the players portrait at a specific seat to display a certain status
	 * @param seat the seat of the player to be updated
	 * @param status the status of the player to be updated
	 */
	public void displayPortrait(int seat, int status) {
		gameDisplay.setPlayerPortrait(seat, status);
	}
	
	/**
	 * Update the players money at a specific seat to display the updated amount
	 * @param seat the seat of the player to be updated
	 * @param money amount of money to display on the screen
	 */
	public void displayPlayerMoney(int seat, String money) {
		gameDisplay.setPlayerMoney(seat, money);
	}
	
	/**
	 * @return the maximum bet value for the table
	 */
	public int getMaxBet(){
		return table.getMaxBet();
	}
	
	/**
	 * Set the maximum bet value to the game display
	 * @param maxBet maximum bet value to be set
	 */
	public void displayMaxBetValue(int maxBet) {
		gameDisplay.setMaxBetValue(maxBet);
	}
	
	/**
	 * Set the ante value to the game display
	 * @param ante value to be set to display
	 */
	public void displayAnteValue(int ante){
		gameDisplay.setAnteValue(ante);
	}
	
	/**
	 * Set the pot value to the game display
	 * @param pot value to be set to display
	 */
	public void displayPotValue(int pot){
		gameDisplay.setPot(pot);
	}
	
	/**
	 * Set the tableID value to that given in the parameter and set it to the game display
	 * @param tableID value to be set 
	 */
	public void setTableID(int tableID) {
		this.tableID = tableID;
		gameDisplay.getFrame().setTitle("Table " + tableID);
	}
	
	/**
	 * Update all the appropriate information that is to be displayed at the table
	 * @param tableID identification number of the table to be refreshed
	 * @throws RemoteException
	 */
	public void refreshTable(int tableID) throws RemoteException {
		setTableID(tableID);
		this.table = serverRemote.getTableInformation(tableID, user.getUsername());
		
		// Update names display.
		this.displayNames();
		
		// Update the pot display.
		displayPotValue(table.getPot());
		
		// Update cards display.
		displayCards();
		
		// Update portraits display.
		displayPortraits();
		
		// Update ante and max bet.
		displayAnteValue(table.getAnte());
		displayMaxBetValue(table.getMaxBet());
		
		// Update player money
		displayPlayerBanks();
		
		// waiting for turn or folded
		if (table.getPlayers().get(0).getStatus() == 0 || table.getPlayers().get(0).getStatus() == -1) {
			gameDisplay.setButtonsInactive();
		}
		
		// in turn
		if (table.getPlayers().get(0).getStatus() == 1) {
			gameDisplay.setButtonsActive();
		}
		
		if (owner != null) {
			gameDisplay.startEnabled(true);
		}
		
		if (table.isOver()) {
			gameDisplay.leaveEnabled(true);
		}
		
		// Refresh chat box
		ArrayList<String> messages = serverRemote.getMessages(tableID);
		System.out.println(messages);
		gameDisplay.updateChat(messages);
	}
	
	/**
	 * set each players respective chip count to the game display
	 */
	private void displayPlayerBanks() {
		for (int i = 0; i < table.getPlayers().size(); i++) {
			displayPlayerMoney(i, "$" + table.getPlayers().get(i).getLastBet() + " / $" + table.getPlayers().get(i).getChips());
		}
		
		for (int i = table.getPlayers().size(); i < 6; i++) {
			displayPlayerMoney(i, "");
		}
	}

	public void displayCards() throws RemoteException {
		ArrayList<Card> temp = table.getPlayers().get(0).getHand().getHand();
		if (!temp.isEmpty()) {
			for (int i = 0; i < temp.size(); i++) {
				table.getPlayers().get(0).getCard(0).setFaceUp(true);
			}
		}
		
		for (int i = 0; i < table.getPlayers().size(); i++) {
			temp = table.getPlayers().get(i).getHand().getHand();
			for (int j = 0; j < temp.size(); j++) {
				System.out.println("Printing card " + temp.get(j) + " to card place " + j + " at seat " + i);
				gameDisplay.setCard(i, j+1, temp.get(j));
			}
		}
		
		
		for (int i = table.getMaxPlayers(); i < 6; i++) {
			for (int j = 1; j <= 5; j++) {
				gameDisplay.setCard(i, j, null);
			}
		}
	}
	
	/**
	 * Display the portraits of each player at the table with appropriate
	 * status portraits
	 * @throws RemoteException
	 */
	public void displayPortraits() throws RemoteException {
		
		for (int i = 0; i < table.getPlayers().size(); i++) {
			System.out.println("User status: " + table.getPlayers().get(i).getStatus() + " for player " + i);
			gameDisplay.setPlayerPortrait(i, table.getPlayers().get(i).getStatus());
		}
		
		for (int i = table.getPlayers().size(); i < 6; i++) {
			if (i < table.getMaxPlayers()) {
				gameDisplay.setPlayerPortrait(i, 2);
			}
			else {
				gameDisplay.setPlayerPortrait(i, 3);
			}
		}
	}
	
	/**
	 * Display the usernames of each player at the table, 
	 * if there is no player at a certain seat, display as OPEN SEAT 
	 * @throws RemoteException
	 */
	public void displayNames() throws RemoteException {
		
		String username;
		for (int i = 1; i < table.getMaxPlayers(); i++) {
			if (i < table.getPlayers().size()) {
				username = table.getPlayers().get(i).getUsername();
				gameDisplay.setPlayerName(i, username);
			}
			else {
				gameDisplay.setPlayerName(i, "OPEN SEAT");
			}
		}

		for (int i = table.getMaxPlayers(); i < 6; i++) {
			displayName(i, null);
		}
		
		gameDisplay.setPlayerName(0, user.getUsername());
	}
	
	/**
	 * The sendCallback method places a callback instance on the server
	 * which allows the server to communicate with the client.
	 * @param tableID A unique identifier of the table in which the client is playing.
	 * @throws RemoteException
	 */
	
	public void sendCallback(int tableID) throws RemoteException {
		ClientCallbackInterface callback = new ClientCallback(this);
		UnicastRemoteObject.exportObject(callback);
		serverRemote.addClient(user.getUsername(), callback);
	}

	/**
	 * Begin the game by calling the startFirstStreet method of the serverRemote class
	 * Display a message saying the game has started in the chatbox
	 * @throws RemoteException
	 */
	public void startFirstStreet() throws RemoteException {
		serverRemote.startFirstStreet(tableID);	
		this.sendMessage("--> First street starting.");
	}
	
	/**
	 * Player calls, make the server remote perform the call method
	 * Send appropriate message to the chat box
	 * @throws RemoteException
	 */
	public void call() throws RemoteException{
		serverRemote.call(tableID);
		this.sendMessage("--> " + user.getUsername() + " has called.");
	}
	
	/**
	 * 
	 * Player raises, make the server remote perform the raise method
	 * Send appropriate message to the chat box
	 * @param bet amount the player wishes to raise
	 * @throws RemoteException
	 */
	public void raise(int bet) throws RemoteException{
		serverRemote.raise(tableID, bet);
		this.sendMessage("--> " + user.getUsername() + " has raised $" + bet);
	}
	
	/**
	 * Player folds, make the server remote perform the fold method
	 * Send appropriate message to the chat box
	 * @throws RemoteException
	 */
	public void fold() throws RemoteException{
		serverRemote.fold(tableID);
		this.sendMessage("--> " + user.getUsername() + " has folded.");
	}
	
	/**
	 * Player goes all in, make the server remote perform the all in method
	 * Send appropriate message to the chat box
	 * @throws RemoteException
	 */
	public void allIn() throws RemoteException{
		serverRemote.allIn(tableID);
		this.sendMessage("--> " + user.getUsername() + " is ALL IN.");
	}

	/**
	 * Display a message to the chat box by calling the serverRemote sendMessage method
	 * @param text string to be sent to all users and displayed in the chatbox
	 * @throws RemoteException
	 */
	public void sendMessage(String text) throws RemoteException {
		serverRemote.sendMessage(tableID, user.getUsername(), text);
	}
}
